package com.phaeton.beemeup.entity;

import java.util.UUID;

import org.andengine.engine.handler.IUpdateHandler;
import org.andengine.engine.handler.timer.ITimerCallback;
import org.andengine.engine.handler.timer.TimerHandler;
import org.andengine.entity.IEntity;
import org.andengine.entity.modifier.MoveModifier;
import org.andengine.entity.modifier.RotationModifier;
import org.andengine.entity.shape.IShape;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.opengl.texture.region.ITiledTextureRegion;

import android.util.Log;

import com.phaeton.beemeup.GameManager;
import com.phaeton.beemeup.constants.SceneConstants;

public abstract class Insect extends AnimatedSprite implements IInsect{
	protected int state = IDLE;
	protected int health = 100;
	protected float speed = 100;
	private float acceleration = 1;
	private IShape target;
	private MoveModifier move;
	private int[] animFlyRight;
	private int[] animFlyLeft;
	private int[] animIdle;
	private long[] animSpeed;
	private int animFrameDuration = 100;
	private int animFrameDurationIdle = 300;
	private boolean stopMove = false;
	private int resource;
	protected GameManager mGameManager;
	
	public Insect(float pX, float pY, ITiledTextureRegion pTiledTextureRegion, final GameManager mGameManager) {
		super(pX, pY, pTiledTextureRegion, mGameManager.getActivity().getVertexBufferObjectManager());
		this.mGameManager = mGameManager;
	}

	@Override
	public void fly() {
		if(target != null){
			if(move != null) unregisterEntityModifier(move);

			double distance = Math.sqrt((target.getX() - this.getX())*(target.getX() - this.getX()) + (target.getY() - this.getY())*(target.getY() - this.getY()));
			float distX = Math.abs(target.getX() - this.getX());
			double angle = (float) Math.atan2(this.getY() - target.getY(), this.getX() - target.getX()) * 180f / (float) Math.PI;
			
			if(distance > 0){
				animSpeed = new long[animFlyRight.length];
				for(int i = 0; i < animFlyRight.length; i++){
					animSpeed[i] = animFrameDuration;
				}
				
				if(target.getX() > this.getX()){
					this.animate(animSpeed, animFlyRight);
					angle -= 180;
				}
				else this.animate(animSpeed, animFlyLeft);
				
				this.registerEntityModifier(new RotationModifier(.5f, this.getRotation(), (float)angle));
				this.registerEntityModifier(move = new MoveModifier((float) (distance*1/speed), this.getX(), target.getX(), this.getY(), target.getY()){
					@Override
					public void onManagedUpdate(final float pSecondsElapsed, final IEntity pItem){
						if((target instanceof Flower && (((Flower) target).getState() & Flower.OCCUPIED) != 0)
								|| (target instanceof Comb && (((Comb) target).getState() & Comb.OCCUPIED) != 0)){
							if((Insect.this.getState() & INJURED) == 0) Insect.this.stop();
							else {
								IShape target = null;
								if(Insect.this instanceof Bee) {
									target = mGameManager.mBeeCombs.values().iterator().next();
									double dis_nearest = Double.MAX_VALUE;
									
									for(final BeeComb comb: mGameManager.getBeeCombs().values()) {
										if((comb.getState() & (IComb.OCCUPIED | IComb.RESERVED | IComb.FIGHT)) == 0) {
											double distance = Math.sqrt( (comb.getX() - getX()) * (comb.getX() - getX()) + 
													(comb.getY() - getY()) * (comb.getY() - getY()));
											
											if(distance < dis_nearest) {
												dis_nearest = distance;
												target = comb;
											}
										}
									}
								}else if(Insect.this instanceof Wasp) {
									target = mGameManager.mWaspCombs.values().iterator().next();
									double dis_nearest = Double.MAX_VALUE;
									
									for(final WaspComb comb: mGameManager.getWaspCombs().values()) {
										if((comb.getState() & (IComb.OCCUPIED | IComb.RESERVED | IComb.SURROUNDED | IComb.FIGHT)) == 0) {
											double distance = Math.sqrt( (comb.getX() - getX()) * (comb.getX() - getX()) + 
													(comb.getY() - getY()) * (comb.getY() - getY()));
											
											if(distance < dis_nearest) {
												dis_nearest = distance;
												target = comb;
											}
										}
									}
								}
								
								if(target != null){
									Insect.this.setTarget(target);
									Insect.this.fly();
								}	
							}
							if(Insect.this instanceof Wasp && ((Wasp)Insect.this).wannaFight()){
								target = null;
								double dis_nearest = Double.MAX_VALUE;
								for(final BeeComb comb: mGameManager.getBeeCombs().values()) {
									if((comb.getState() & (IComb.OCCUPIED | IComb.RESERVED | IComb.FIGHT)) == 0) {
										double distance = Math.sqrt( (comb.getX() - getX()) * (comb.getX() - getX()) + 
												(comb.getY() - getY()) * (comb.getY() - getY()));
										
										if(distance < dis_nearest) {
											dis_nearest = distance;
											target = comb;
										}
									}
								}
								if(target != null){
									Insect.this.setTarget(target);
									Insect.this.fly();
								}
							}
						}
						if(Insect.this instanceof Wasp && target instanceof Bee){
							for(Fight fight: mGameManager.getFights()){
								UUID beeID = null;
								for(UUID lBeeID: mGameManager.getBees().keySet()){
									if(mGameManager.getBees().get(lBeeID).equals(target)){
										beeID = lBeeID;
										break;
									}
								}
								if(fight.getFightingBees().contains(beeID)){
									stop();
									setTarget(fight);
									fly();
								}
							}
						}
						if(Insect.this instanceof Bee && target instanceof Wasp){
							for(Fight fight: mGameManager.getFights()){
								UUID waspID = null;
								for(UUID lWaspID: mGameManager.getWasps().keySet()){
									if(mGameManager.getBees().get(lWaspID).equals(target)){
										waspID = lWaspID;
										break;
									}
								}
								if(fight.getFightingWasps().contains(waspID)){
									stop();
									setTarget(fight);
									fly();
								}
							}
						}
						if(target.getX() != this.getToValueA() || target.getY() != this.getToValueB()){
							double distance = Math.sqrt((target.getX() - Insect.this.getX())*(target.getX() - Insect.this.getX()) + (target.getY() - Insect.this.getY())*(target.getY() - Insect.this.getY()));
							this.reset((float) (distance*1/speed), Insect.this.getX(), target.getX(), Insect.this.getY(), target.getY());
						}
						if(target instanceof Fight) {
							if(target == null || !mGameManager.getFights().contains(target)){
								Insect.this.stop();
							}
						}
						super.onManagedUpdate(pSecondsElapsed, pItem);
					}
					@Override
					protected void onModifierFinished(IEntity pItem){
						animSpeed = new long[animIdle.length];
						for(int i = 0; i < animFlyRight.length; i++){
							animSpeed[i] = animFrameDurationIdle;
						}
						Insect.this.animate(animSpeed, animIdle);
						Insect.this.setRotation(0);

						Insect.this.removeState(FLY);
						Insect.this.addState(ARRIVING);
						Insect.this.registerUpdateHandler(new TimerHandler(.1f, false, new ITimerCallback() {							
							@Override
							public void onTimePassed(TimerHandler pTimerHandler) {
								Insect.this.removeState(ARRIVING);
								Insect.this.unregisterUpdateHandler(pTimerHandler);
							}
						}));
						
						if(Insect.this instanceof Wasp && target instanceof BeeComb){
							UUID beeCombID = null;
							for(UUID lBeeCombID: mGameManager.getBeeCombs().keySet()){
								//Log.e("Insect", "BeeComb: " + lBeeCombID);
								if(mGameManager.getBeeCombs().get(lBeeCombID).equals(target)){
									beeCombID = lBeeCombID;
									break;
								}
							}
							((Comb)target).addState(Comb.ATTACKED);
							Insect.this.addState(Insect.FIGHT);
							//((BeeComb)target).damageComb(beeCombID);
						}
						
						if(Insect.this instanceof Bee && target instanceof WaspComb){
							UUID waspCombID = null;
							for(UUID lWaspCombID: mGameManager.getWaspCombs().keySet()){
								//Log.e("Insect", "BeeComb: " + lBeeCombID);
								if(mGameManager.getWaspCombs().get(lWaspCombID).equals(target)){
									waspCombID = lWaspCombID;
									break;
								}
							}
							((Comb)target).addState(Comb.ATTACKED);
							Insect.this.addState(Insect.FIGHT);
						}
						
						if(target instanceof Flower){
							((Flower)target).removeState(Flower.RESERVED);
							((Flower)target).occupy(Insect.this);
						}
						if(target instanceof Comb){
							((Comb)target).removeState(Comb.RESERVED);
							((Comb)target).occupy(Insect.this);
						}							
						
						if(Insect.this instanceof Wasp && target instanceof Bee){
							Fight fight = new Fight(target.getX(), target.getY(), mGameManager);
							UUID beeID = null;
							UUID waspID = null;
							for(UUID lBeeID: mGameManager.getBees().keySet()){
								if(mGameManager.getBees().get(lBeeID).equals(target)){
									beeID = lBeeID;
									break;
								}
							}
							for(UUID lWaspID: mGameManager.getWasps().keySet()){
								if(mGameManager.getWasps().get(lWaspID).equals((Wasp)Insect.this)) {
									waspID = lWaspID;
									break;
								}
							}
							fight.addBee(beeID);
							fight.addWasp(waspID);
							mGameManager.addFight(fight);
							fight.startFight();
						}
						if(Insect.this instanceof Bee && target instanceof Wasp){
							Fight fight = new Fight(target.getX(), target.getY(), mGameManager);
							UUID beeID = null;
							UUID waspID = null;
							for(UUID lBeeID: mGameManager.getBees().keySet()){
								if(mGameManager.getBees().get(lBeeID).equals((Bee)Insect.this)) {
									beeID = lBeeID;
									break;
								}
							}
							for(UUID lWaspID: mGameManager.getWasps().keySet()){
								if(mGameManager.getWasps().get(lWaspID).equals(target)) {
									waspID = lWaspID;
									break;
								}
							}
							fight.addBee(beeID);
							fight.addWasp(waspID);
							mGameManager.addFight(fight);
							fight.startFight();
						}
						if(target instanceof Fight){
							if(Insect.this instanceof Bee){
								UUID beeID = null;
								for(UUID lBeeID: mGameManager.getBees().keySet()){
									if(mGameManager.getBees().get(lBeeID).equals((Bee)Insect.this)) {
										beeID = lBeeID;
										break;
									}
								}
								((Fight) target).addBee(beeID);
							}
							if(Insect.this instanceof Wasp){
								UUID waspID = null;
								for(UUID lWaspID: mGameManager.getWasps().keySet()){
									if(mGameManager.getWasps().get(lWaspID).equals((Wasp)Insect.this)) {
										waspID = lWaspID;
										break;
									}
								}
								((Fight) target).addWasp(waspID);
							}
						}
						if(Insect.this instanceof Bee && target instanceof Bee){
							if(Insect.this.getResource() > 0 && ((Insect)target).getHealth() < ((Insect)target).getMaxHealth()){
								Insect.this.addState(REGENERATION);
								((Insect) target).addState(REGENERATION);
								
								final AnimatedSprite redCross = new AnimatedSprite(target.getX(), target.getY() - 50,mGameManager.getActivity().getSceneManager().getTiledTexture("Heal"),mGameManager.getActivity().getVertexBufferObjectManager());
								mGameManager.getLevel().getChildByIndex(SceneConstants.LAYER_GUI).attachChild(redCross);
								
								target.registerUpdateHandler(new TimerHandler(2,true,new ITimerCallback(){

									@Override
									public void onTimePassed(
											TimerHandler pTimerHandler) {
										Insect.this.changeResource(-1);
										((Insect) target).changeHealth(20);
										
										if(Insect.this.getResource() <= 0 || ((Insect)target).getHealth() >= ((Insect)target).getMaxHealth()){
											target.unregisterUpdateHandler(pTimerHandler);
											Insect.this.removeState(REGENERATION);
											((Insect) target).removeState(REGENERATION);

											mGameManager.getActivity().runOnUpdateThread(new Runnable() {
												@Override
												public void run() {
												/* Now it is save to remove the entity! */
													redCross.detachSelf();
												}
											});
										}
									}
									
								}));
							}
						}
						super.onModifierFinished(pItem);
					}
				});
				Insect.this.addState(FLY);
			}
		}
	}

	@Override
	public void stop() {
		//Log.e("Insect", "insect "+this+" stopped");
		if(move != null){
			unregisterEntityModifier(move);
		}

		Insect.this.removeState(FLY);
		
		if(target instanceof Flower){
			((Flower)target).removeState(Flower.RESERVED);
		}
		if(target instanceof Comb){
			((Comb)target).removeState(Comb.RESERVED);
		}
		
		animSpeed = new long[animIdle.length];
		for(int i = 0; i < animFlyRight.length; i++){
			animSpeed[i] = animFrameDurationIdle;
		}
		this.animate(animSpeed, animIdle);
	}
	
	public void injure(){
		this.addState(Insect.INJURED);
		this.setAnimData(4, 5, 4, 5, 6, 7);
		this.animate(animSpeed, animIdle);
		
		Log.e("Insect", "injured insect "+Insect.this);
		IShape target = null;
		
		if(Insect.this instanceof Bee) {
			target = mGameManager.mBeeCombs.values().iterator().next();
			double dis_nearest = Double.MAX_VALUE;
			
			for(final BeeComb comb: mGameManager.getBeeCombs().values()) {
				if((comb.getState() & (IComb.OCCUPIED | IComb.RESERVED | IComb.FIGHT)) == 0) {
					double distance = Math.sqrt( (comb.getX() - getX()) * (comb.getX() - getX()) + 
							(comb.getY() - getY()) * (comb.getY() - getY()));
					
					if(distance < dis_nearest) {
						dis_nearest = distance;
						target = comb;
					}
				}
			}
		}else if(Insect.this instanceof Wasp) {
			target = mGameManager.mWaspCombs.values().iterator().next();
			double dis_nearest = Double.MAX_VALUE;
			
			for(final WaspComb comb: mGameManager.getWaspCombs().values()) {
				if((comb.getState() & (IComb.OCCUPIED | IComb.RESERVED | IComb.SURROUNDED | IComb.FIGHT)) == 0) {
					double distance = Math.sqrt( (comb.getX() - getX()) * (comb.getX() - getX()) + 
							(comb.getY() - getY()) * (comb.getY() - getY()));
					
					if(distance < dis_nearest) {
						dis_nearest = distance;
						target = comb;
					}
				}
			}
		}
		
		if(target != null){
			Insect.this.setTarget(target);
			Insect.this.fly();
		}	
	}
	
	public void heal(){
		this.removeState(IInsect.REGENERATION);
		this.removeState(IInsect.INJURED);
		this.setAnimData(0, 1, 0, 1, 2, 3);
		this.animate(animSpeed, animIdle);
	}

	@Override
	public float getSpeed() {
		return speed;
	}

	@Override
	public float getAcceleration() {
		return acceleration;
	}

	public IShape getTarget(){
		return target;
	}

	public int getHealth() {
		return health;
	}
	
	@Override
	public void setSpeed(float speed) {
		this.speed = speed;
	}
	
	public void setHealth(int health) {
		this.health = health;
	}
	
	public void changeHealth(int i) {
		health += i;
	}

	@Override
	public void setAcceleration(float acceleration) {
		this.acceleration = acceleration;
	}

	@Override
	public void setTarget(IShape target) {
		if(this.target instanceof Flower){
			((Flower) this.target).removeState(Flower.RESERVED);
			((Flower) this.target).deOccupy();
		}
		if(this.target instanceof Comb){
			((Comb) this.target).removeState(Comb.RESERVED);
			((Comb) this.target).deOccupy();
		}
		this.target = target;
	}
	
	public void setAnimData(int idleStart, int idleEnd, int flyRightStart, int flyRightEnd, int flyLeftStart, int flyLeftEnd){
		animIdle = new int[idleEnd - idleStart + 1];
		animFlyRight = new int[flyRightEnd - flyRightStart + 1];
		animFlyLeft = new int[flyLeftEnd - flyLeftStart + 1];
		
		for(int i = idleStart; i <= idleEnd; i++){
			animIdle[i - idleStart] = i;
		}
		
		for(int i = flyRightStart; i <= flyRightEnd; i++){
			animFlyRight[i - flyRightStart] = i;
		}
		
		for(int i = flyLeftStart; i <= flyLeftEnd; i++){
			animFlyLeft[i - flyLeftStart] = i;
		}
	}
	
	public void changeResource(int i){
		resource += i;
	}
	
	public int getResource(){
		return resource;
	}
	
	public void setResource(int i){
		resource = i;
	}
	
	public void addState(int state) {
		this.state |= state;
	}
	
	public void removeState(int state) {
		this.state &= ~state;
	}
	
	public int getState() {
		return state;
	}
	
	public abstract int getMaxHealth();
}
